package FlowGraph;

import java.util.*;

import antlr.collections.AST;
import antlr.*;

import SymbolTable.*;
import JavaGrammar.*;
import JavaGrammar.TreeScanners.*;
import FlowGraph.EdgeInfo.*;
import FlowGraph.Nodes.*;
import FlowGraph.StatementInfo.*;
import util.*;
import FlowGraph.Transformations.*;
import db.ClassData;

//////////////////////////////////////////////////////////////////////////
//// FlowGraphFactory
/**
        Essa classe possui os metodos para construir um novo FlowGraph.

@author Ivan Jeukens
@version v 0.1 19/04/2002
*/
public class FlowGraphFactory {

    public FlowGraphFactory() {
    }

    ///////////////////////////////////////////////////////////////////
    ////                        public  methods                    ////

    public void build(FlowGraph fg, AST root, ClassData cinfo,
            SymbolTable methodParameters) {
        _currentFG = fg;
        _currentInfo = cinfo;

        _build(root);
        _check();

        FlowGraphNode source = _currentFG.getSource();
        FlowGraphNode sink = _currentFG.getSink();

        source.setSymbolTable(methodParameters, true);
        sink.setSymbolTable(methodParameters, false);

        _parentStack = new Stack();

        _buildSymbolTable(methodParameters, source.getOutputNode(0), sink);
        if(_parentStack.size() > 0) {
            System.out.println("BUILD -> parentStack > 0");
        }
        // _collectIdentifiers();

        _currentFG.setUniqueCount(_uniqueCount);
        _currentFG.setUniqueSCCount(_uniqueCount);

        // As Transformacoes. Uma implementacao melhor
        // pode juntar todos esses metodos em um

        _extractMethodCalls();
        _modifyForLoops();
        _extractNewExpr();
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private methods                    ////

    // retira de todos os loops for do grafo, as expressoes de
    // inicializacao e de proxima iteracao. Isso e devido ao
    // processo de transformacao para SSA.
    private void _modifyForLoops() {
        ForInitializationExtraction initex =
            new ForInitializationExtraction(_currentFG);
        ForIteratorExtraction iteex =
            new ForIteratorExtraction(_currentFG);
        for(int i = 0;i < _currentFG.size();i++) {
            FlowGraphNode node = _currentFG.getNode(i);
            if(node.getType() == FlowGraphNode.FOR_BEGIN) {
                ForBeginNode fbeg = (ForBeginNode) node;
                initex.transform(fbeg);
                iteex.transform(fbeg);
            }
        }
    }

    // extrai chamada de metodos de expressoes e statements.
    private void _extractMethodCalls() {
        MethodExtracter extracter = new MethodExtracter();
        extracter.setFlowGraph(_currentFG);
        extracter.setSkipMCTable(_skipMCTable);
        extracter.setCurrentInfo(_currentInfo);

        //extracter.setVarIdentifiers(_collector.getIdentifiers());

        // Como os novos nos sao introduzidos no fim do _nodes, tambem
        // vou processa-los.
        for(int i = 0;i < _currentFG.size();i++) {
            FlowGraphNode node = _currentFG.getNode(i);
            switch(node.getType()) {

                case FlowGraphNode.BASIC_BLOCK: {
                    BasicBlockNode bb = (BasicBlockNode) node;
                    extracter.setFlowGraphNode(bb);
                    int counter = 0;
                    int statSize = bb.statementsSize();
                    do {
                        extracter.resetBBSplit();
                        for(int j = 0;j < bb.statementsSize();j++) {
                            AST stat = bb.getStatement(j);
                            counter++;
                            extracter.setStatementIndex(j);
                            try {
                               extracter.statement(stat);
                            }
                            catch(RecognitionException ex) {
                                System.out.println("ExtractMethodCall : BB " +
                                    ex);
                            }
                            if(extracter.getBBSplit()) {
                                break;
                            }
                        }
                    } while(counter < statSize);

                } break;

                case FlowGraphNode.DO_CONDITION: {
                    extracter.setFlowGraphNode(node);
                    try {
                        extracter.statement(((DoConditionNode)node).getCondition());
                    }
                    catch(RecognitionException ex) {
                        System.out.println("ExtractMethodCall : DO_COND" + ex);
                    }
                } break;
                
                case FlowGraphNode.THROW: {
                    extracter.setFlowGraphNode(node);
                    try {
                        extracter.statement(((ThrowNode)node).getExpression());
                    }
                    catch(RecognitionException ex) {
                       System.out.println("ExtractMethodCall : Throw -> " + ex);
                    }
                } break;
                
                case FlowGraphNode.RETURN: {
                    extracter.setFlowGraphNode(node);
                    AST stat = ((ReturnNode)node).getExpression();
                    if(stat == null) continue;
                    try {
                        extracter.statement(stat);
                    }
                    catch(RecognitionException ex) {
                        System.out.println("ExtractMethodCall : RETURN -> " + 
                            ex);
                    }
                } break;

                case FlowGraphNode.FOR_BEGIN: {
                    extracter.setFlowGraphNode(node);
                    ForBeginNode fbeg = (ForBeginNode) node;
                    try {
                        extracter.statement(fbeg.getInit());
                    }
                    catch(RecognitionException ex) {
                       System.out.println("ExtractMethodCall : FOR_BEG -> " + 
                           ex);
                    }

                    try {
                        extracter.statement(fbeg.getCondition());
                    }
                    catch(RecognitionException ex) {
                       System.out.println("ExtractMethodCall : FOR_COND -> " + 
                           ex);
                    }

                    try {
                        extracter.statement(fbeg.getIterator());
                    }
                    catch(RecognitionException ex) {
                       System.out.println("ExtractMethodCall : FOR_ITE -> " + 
                           ex);
                    }
                } break;

                case FlowGraphNode.WHILE_BEGIN: {
                    extracter.setFlowGraphNode(node);
                    try {
                        extracter.statement(((WhileBeginNode)node).getCondition());
                    }
                    catch(RecognitionException ex) {
                        System.out.println("ExtractMethodCall : WHILE_BEGIN -> " + 
                            ex);
                    }
                } break;
                
                case FlowGraphNode.SWITCH_BEGIN: {
                    extracter.setFlowGraphNode(node);
                    try {
                        extracter.statement(
                            ((SwitchBeginNode)node).getExpression());
                    }
                    catch(RecognitionException ex) {
                       System.out.println("ExtractMethodCall : SW_BEGIN -> " + 
                           ex);
                    }
                } break;
                
                case FlowGraphNode.IF_BEGIN: {
                    extracter.setFlowGraphNode(node);
                    try {
                        extracter.statement(((IfBeginNode)node).getCondition());
                    }
                    catch(RecognitionException ex) {
                        System.out.println("ExtractMethodCall : If_BEGIN -> " +
                            ex);
                    }
                } break;

                case FlowGraphNode.SYNC_BEGIN: {
                    extracter.setFlowGraphNode(node);
                    try {
                        extracter.statement(
                            node.getAbsTreeNode().getFirstChild());
                    }
                    catch(RecognitionException ex) {
                        System.out.println("ExtractMethodCall : SYNC_BEGIN -> "
                            + ex);
                    }
                } break;

                case FlowGraphNode.METHOD_CALL: {
                    extracter.setFlowGraphNode(node);
                    AST ast = (AST) _skipMCTable.get(node);
                    extracter.setSkipMC(ast);
                    try {
                        extracter.statement(node.getAbsTreeNode());
                    }
                    catch(RecognitionException ex) {
                        System.out.println("ExtractMethodCall : MC -> " 
                            + ex);
                    }
                } break;

                default:
            }
        }
    }

    // server para dar nomes distintos as novas variaveis criadas pela extracao
    // de comandos new.
    private int _newvarCounter = 0;

    /* Remove uma expressao de new de dentro de um statement, criando um BB ou
    um novo statement em um BB. Isso e usado para facilitar a criacao das
    arestas causadas por exceptions. */
    // !!!!!!!! Falta lidar com nos de classes internas
    private void _extractNewExpr() {
        HashMap dontProcess = new HashMap();

        NewExprExtracter extracter = new NewExprExtracter();  // um TreeScanner
        for(int i = 0;i < _currentFG.size();i++) {
            FlowGraphNode node = _currentFG.getNode(i);
            if(dontProcess.containsKey(node)) continue;

            NewStatementExtraction extr = new NewStatementExtraction(_currentFG,
                _currentInfo);

            switch(node.getType()) {

                case FlowGraphNode.BASIC_BLOCK: {
                    BasicBlockNode bb = (BasicBlockNode) node;
                    int count = bb.statementsSize();
                    int index = 0;
                    while(count > 0) {
                        LinkedAST stat = (LinkedAST) bb.getStatement(index);
                        LinkedAST parentAST = (LinkedAST) stat.getParent();
                        try {
                            extracter.statement(stat);
                        }
                        catch(RecognitionException ex) {
                            System.out.println("Extract New Expression : BB -> " +
                                ex);
                        }
                        LinkedList exprs = extracter.getNewExpressions();
                        if(exprs.size() == 0) {
                            count--;
                            index++;
                            continue;
                        }

                        // Nao preciso processar quando for um statement que eu 
                        // introduzi.
                        StatementInfo info = bb.getStatementInfo(index);
                        if(info != null && info.getType() == StatementInfo.VARDEF) {
                            VarDefStatementInfo vardef = (VarDefStatementInfo) info;
                            if(vardef.isNew() && exprs.size() == 1) {
                                index++;
                                count--;
                                continue;
                            }
                        }

                        LinkedAST previousAST = parentAST;
                        SymbolTable st = bb.getSymbolTable();
                        SymbolTable parentST = null;

                        // Se o BB nao tiver uma symboltable, tem que criar,
                        // senao introduz a definicao no antes da hora.
                        if(st.getOwner() != bb) {
                            parentST = st;
                            st = new SymbolTable();
                        }

                        for(int k = 0;k < exprs.size();k++) {
                            LinkedAST exprtmp = (LinkedAST) exprs.get(k);
                            if(_currentFG.isConstructor()) {
                                _extractPortDirection(stat, exprtmp);
                            }

                            LinkedAST newexpr1 = _createNewExpr(exprtmp);
                            bb.addStatement(newexpr1, index+k);
                            if(k == 0) {
                                if(parentAST.getFirstChild() == stat) {
                                    previousAST.setFirstChild(newexpr1);                                                                        
                                }
                                else {
                                    previousAST.setNextSibling(newexpr1);
                                }
                            }
                            else {
                                previousAST.setNextSibling(newexpr1);
                            }
                            newexpr1.setParent(previousAST);
                            previousAST = newexpr1;

                            VarDefStatementInfo vardef = 
                                new VarDefStatementInfo(bb, newexpr1);
                            vardef.setIsNew(true);
                            bb.setStatementInfo(vardef, index+k);

                            STLocalVariableEntry entry = 
                                STLocalVariableEntry.generateSTEntry(
                                newexpr1, _currentInfo.getName(),
                                _currentInfo.getPackage(),
                                _currentInfo.getImports());

		            st.putElement(entry.getName(), entry);
                        }
                        // Tem nova variavel, entrao seta a nova ST

                        if(st.getOwner() == null) {
                            SymbolTable.updateNodesST(bb, _currentFG.getSink(),
                                st, parentST);
                            st.link(parentST);
                            st.setOwner(bb);
                            bb.setSymbolTable(st, true);
                        }

                        previousAST.setNextSibling(stat);
                        stat.setParent(previousAST);

                        if(exprs.size() <= 1) {
                            index += 2;
                        }
                        else {
                            index += exprs.size() + 1;
                        }
                        count--;
                    }
                } break;

                case FlowGraphNode.DO_CONDITION: {
                    extr.setNewVarCounter(_newvarCounter);
                    extr.extractNewDoStatement(((DoConditionNode)node).getCondition(), dontProcess,
                        extracter, node);
                    _newvarCounter = extr.getNewVarCounter();
                } break;

                case FlowGraphNode.THROW: {
                    extr.setNewVarCounter(_newvarCounter);
                    extr.extractNewStatement(((ThrowNode)node).getExpression(), dontProcess,
                        extracter, node);
                    _newvarCounter = extr.getNewVarCounter();
                } break;

                case FlowGraphNode.RETURN: {
                    extr.setNewVarCounter(_newvarCounter);
                    extr.extractNewStatement(((ReturnNode)node).getExpression(), dontProcess,
                        extracter, node);
                    _newvarCounter = extr.getNewVarCounter();
                } break;

                case FlowGraphNode.FOR_BEGIN: {
                    ForBeginNode fbeg = (ForBeginNode) node;
                    extr.setNewVarCounter(_newvarCounter);

                    extr.extractNewStatement(fbeg.getInit(), dontProcess,
                        extracter, node);

                    extr.extractNewStatement(fbeg.getCondition(), dontProcess,
                        extracter, node);

                    extr.extractNewStatement(fbeg.getIterator(), dontProcess,
                        extracter, node);

                    _newvarCounter = extr.getNewVarCounter();
                } break;

                case FlowGraphNode.WHILE_BEGIN: {
                    extr.setNewVarCounter(_newvarCounter);
                    extr.extractNewStatement(((WhileBeginNode)node).getCondition(), dontProcess,
                        extracter, node);
                    _newvarCounter = extr.getNewVarCounter();
                } break;
                
                case FlowGraphNode.SWITCH_BEGIN: {
                    extr.setNewVarCounter(_newvarCounter);
                    extr.extractNewStatement(((SwitchBeginNode)node).getExpression(), dontProcess,
                        extracter, node);
                    _newvarCounter = extr.getNewVarCounter();
                } break;

                case FlowGraphNode.IF_BEGIN: {
                    extr.setNewVarCounter(_newvarCounter);
                    extr.extractNewStatement(((IfBeginNode)node).getCondition(), dontProcess,
                        extracter, node);
                    _newvarCounter = extr.getNewVarCounter();
                } break;

                case FlowGraphNode.SYNC_BEGIN: {
                    extr.setNewVarCounter(_newvarCounter);
                    extr.extractNewStatement(node.getAbsTreeNode().getFirstChild(), dontProcess,
                        extracter, node);
                    _newvarCounter = extr.getNewVarCounter();
                } break;

                case FlowGraphNode.METHOD_CALL: {
                    extr.setNewVarCounter(_newvarCounter);
                    extr.extractNewStatement(node.getAbsTreeNode(), dontProcess,
                        extracter, node);
                    _newvarCounter = extr.getNewVarCounter();
                } break;

                default:
            }
        }
    }

    private void _extractPortDirection(LinkedAST stat, LinkedAST expr) {
        STPortEntry port = null;
        if(stat.getType() == JavaTokenTypes.EXPR) {
            AST var = stat.getFirstChild();
            if(var.getType() == JavaTokenTypes.ASSIGN) {
                var = var.getFirstChild();
                if(var.getType() == JavaTokenTypes.IDENT) {
                    port = _currentInfo.getPort(var.getText());
                }
            }
        }
        if(port == null) return;

        AST v = expr.getFirstChild();
        if(v != null && (v.getText().equals("TypedIOPort") ||
                v.getText().equals("IOPort"))) {
            v = v.getNextSibling();
            if(v.getType() == JavaTokenTypes.ELIST) {
                v = v.getFirstChild();  // primeiro arg;
                if(v == null) return; 
                v = v.getNextSibling();
                if(v == null) return;
                v = v.getNextSibling();   //E input?
                if(v == null) return;
                if(v.getFirstChild().getText().equals("true")) {
                    port.setInput(true);
                }
                if(v == null) return;
                v = v.getNextSibling();
                if(v.getFirstChild().getText().equals("true")) {
                    port.setOutput(true);
                }
            }
        }
    }
    
    /* Cria um novo ast Type var1 = expr1 e substitui a entrada expr1 em
    expr2 por var1 */
    private LinkedAST _createNewExpr(LinkedAST expr1) {
        CommonToken tok = new CommonToken(JavaTokenTypes.VARIABLE_DEF, 
            new String("VARIABLE_DEF"));
        LinkedAST vardef = new LinkedAST(tok);

        tok = new CommonToken(JavaTokenTypes.MODIFIERS, new String("MODIFIERS"));
        LinkedAST ast = new LinkedAST(tok);
        vardef.addChild(ast);
        ast.setParent(vardef);

        tok = new CommonToken(JavaTokenTypes.TYPE, new String("TYPE"));
        LinkedAST ast2 = new LinkedAST(tok);

        ast.setNextSibling(ast2);
        ast2.setParent(ast);

        NameCollector nc = new NameCollector();
        Util.inorderTraverse(expr1.getFirstChild(), nc);
        tok = new CommonToken(JavaTokenTypes.IDENT, nc.getName());
        LinkedAST ast3 = new LinkedAST(tok);
	
        ast2.addChild(ast3);
	ast3.setParent(ast2);

        String varname = new String("_newtmp" + _newvarCounter);
	tok = new CommonToken(JavaTokenTypes.IDENT, varname);
	LinkedAST ast4 = new LinkedAST(tok);

	ast2.setNextSibling(ast4);
	ast4.setParent(ast2);

        tok = new CommonToken(JavaTokenTypes.ASSIGN, "=");
        LinkedAST ast5 = new LinkedAST(tok);

        ast4.setNextSibling(ast5);
        ast5.setParent(ast4);

        tok = new CommonToken(JavaTokenTypes.EXPR, "EXPR");
        LinkedAST ast6 = new LinkedAST(tok);

        ast5.addChild(ast6);
        ast6.setParent(ast5);

        LinkedAST newexpr1 = Util.dupTree(expr1);
        ast6.addChild(newexpr1);
        newexpr1.setParent(ast6);

        expr1.setType(JavaTokenTypes.IDENT);
        expr1.setText(varname);
        expr1.setFirstChild(null);

        _newvarCounter++;

        return vardef;
    }

    private SymbolTable _createNodeST(FlowGraphNode node, SymbolTable st, 
            SymbolTable parentST) {
        _searchDeclarations(node, st);
        if(st.size() == 0) {
            node.setSymbolTable(parentST, false);
            return parentST;
        }
        else {
            node.setSymbolTable(st, true);
            st.link(parentST);
            return st;
        }
    }

    private Stack _parentStack;

    private SymbolTable _buildSymbolTable(SymbolTable rootST, FlowGraphNode node,
            FlowGraphNode stopNode) {
        SymbolTable parentST = rootST;
        while(node.getType() != FlowGraphNode.SINK && node != stopNode) {
            if(node.getOpenedScopes() > 0) {
                for(int i = 0;i < node.getOpenedScopes();i++) {
                    _parentStack.push(parentST);
                }
            }

            SymbolTable st = new SymbolTable();
            switch(node.getType()) {
                case FlowGraphNode.METHOD_CALL:
                case FlowGraphNode.BASIC_BLOCK: {
                    parentST = _createNodeST(node, st, parentST);
                } break;

                case FlowGraphNode.IF_BEGIN: {
                    IfBeginNode ifbeg = (IfBeginNode) node;
                    IfEndNode end = ifbeg.getIfEnd();
                    
                    if(end != null) {
                        end.setSymbolTable(parentST, false);
                    }
                    st = _createNodeST(ifbeg, st, parentST);

                    node = ifbeg.getThenNode();
                    if(node != null && node != end) {
                        _buildSymbolTable(st, node, end);
                    }
                    node = ifbeg.getElseNode();
                    if(node.getType() != FlowGraphNode.IF_END) {
                        _buildSymbolTable(st, node, end);
                    }

                    if(end == null) return null;
                    node = end;
                } break;

                case FlowGraphNode.WHILE_BEGIN: {
                    WhileBeginNode beg = (WhileBeginNode) node;
                    WhileEndNode end = beg.getWhileEnd();

                    end.setSymbolTable(parentST, false);
                    st = _createNodeST(beg, st, parentST);

                    node = beg.getContinueNode();
                    if(node != beg) {
                        _buildSymbolTable(st, node, beg);
                    }

                    node = end;
                } break;

                case FlowGraphNode.DO_BEGIN: {
                    DoBeginNode beg = (DoBeginNode) node;
                    DoEndNode end = beg.getDoEnd();
                    DoConditionNode cond = beg.getConditionNode();
                    cond.setSymbolTable(parentST, false);
                    end.setSymbolTable(parentST, false);
                    
                    st = _createNodeST(beg, st, parentST);

                    node = node.getOutputNode(0);
                    if(node.getType() != FlowGraphNode.DO_CONDITION) {
                        _buildSymbolTable(st, node, cond);
                    }
                    node = end;
                } break;
                
                case FlowGraphNode.FOR_BEGIN: {
                    ForBeginNode beg = (ForBeginNode) node;
                    ForEndNode end = beg.getForEnd();

                    end.setSymbolTable(parentST, false);
                    st = _createNodeST(beg, st, parentST);

                    node = beg.getContinueNode();
                    if(node != beg) {
                        _buildSymbolTable(st, node, beg);
                    }
                    node = end;
                } break;

                case FlowGraphNode.BREAK: {
                    _createNodeST(node, st, parentST);
                    if(node.getClosedScopes() > 0) {
                        for(int i = 0;i < node.getClosedScopes();i++) {
                            parentST = (SymbolTable) _parentStack.pop();
                        }
                    }
                    return parentST;
                }

                case FlowGraphNode.CONTINUE: {
                    _createNodeST(node, st, parentST);
                    if(node.getClosedScopes() > 0) {
                        for(int i = 0;i < node.getClosedScopes();i++) {
                            parentST = (SymbolTable) _parentStack.pop();
                        }
                    }
                    return parentST;
                }

                case FlowGraphNode.RETURN: {
                    _createNodeST(node, st, parentST);
                    if(node.getClosedScopes() > 0) {
                        for(int i = 0;i < node.getClosedScopes();i++) {
                            parentST = (SymbolTable) _parentStack.pop();
                        }
                    }
                    return parentST;
                }

                case FlowGraphNode.THROW: {
                    _createNodeST(node, st, parentST);
                    if(node.getClosedScopes() > 0) {
                        for(int i = 0;i < node.getClosedScopes();i++) {
                            parentST = (SymbolTable) _parentStack.pop();
                        }
                    }
                    return parentST;
                }

                case FlowGraphNode.SWITCH_BEGIN: {
                    SwitchBeginNode beg = (SwitchBeginNode) node;
                    SwitchEndNode end = beg.getSwitchEnd();

                    end.setSymbolTable(parentST, false);
                    st = _createNodeST(beg, st, parentST);

                    for(int k = 0;k < beg.outputSize();k++) {
                        FlowGraphEdge edge = beg.getOutputEdge(k);
                        node = edge.getTo();
                        FlowGraphNode cend = beg.getCaseEndNode(k);
                        cend.setSymbolTable(parentST, false);

                        if(node.getType() != FlowGraphNode.SWITCH_END) {
                            st = _buildSymbolTable(st, node, cend);
                        }
                    }
                    node = end;
                } break;

                case FlowGraphNode.TRY_BEGIN: {
                    TryBeginNode beg = (TryBeginNode) node;
                    TryEndNode end = beg.getTryEnd();
                    FlowGraph cont = beg.getContainer();

                    if(end != null) {
                        end.setSymbolTable(parentST, false);
                    }
                    st = _createNodeST(beg, st, parentST);

                    node = beg.getOutputNode(0);
                    _buildSymbolTable(st, node, end);

                    for(int k = 0;k < beg.catchSize();k++) {
                        CatchBeginNode cbeg = beg.getCatch(k);
                        node = cbeg.getOutputNode(0);
                        CatchEndNode cend = cbeg.getCatchEnd();

                        if(cend != null) {
                            cend.setSymbolTable(parentST, false);
                        }
                        st = new SymbolTable();
                        st = _createNodeST(cbeg, st, parentST);
                        if(node != cend) {
                            _buildSymbolTable(st, node, cend);                                                        
                        }
                    }

                    FinallyBeginNode fbeg = beg.getFinally();
                    if(fbeg != null) {
                        node = fbeg.getOutputNode(0);
                        FinallyEndNode fend = fbeg.getFinallyEnd();
                        fend.setSymbolTable(parentST, false);
                        st = new SymbolTable();
                        st = _createNodeST(fbeg, st, parentST);
                        if(node != fend) {
                            _buildSymbolTable(st, node, fend);
                        }
                        node = fend;
                    }
                    else {
                        // Ele tem que ter finally ou catch.
                        node = end;
                        // O end pode ser null, no caso que o corpo do try
                        // e todos os catches tem return no fim. Entao tem
                        // que retornar.
                        if(end == null) {
                            return parentST;
                        }
                    }
                } break;

                case FlowGraphNode.SYNC_BEGIN: {
                    SyncBeginNode beg = (SyncBeginNode) node;
                    SyncEndNode end = beg.getSyncEnd();

                    end.setSymbolTable(parentST, false);
                    st = _createNodeST(beg, st, parentST);

                    node = beg.getOutputNode(0);
                    _buildSymbolTable(st, node, end);

                    node = end;
                } break;
                
                case FlowGraphNode.INNER_CLASS: {
                    parentST = _createNodeST(node, st, parentST);
                } break;

                default: {
                    return parentST;
                }
            }
            if(node.getClosedScopes() > 0) {
                for(int i = 0;i < node.getClosedScopes();i++) {
                    parentST = (SymbolTable) _parentStack.pop();
                }
            }
            node = node.getOutputNode(0);
        }
        return parentST;
    }

    private void _searchDeclarations(FlowGraphNode node, SymbolTable st) {
        switch(node.getType()) {
            case FlowGraphNode.BASIC_BLOCK: {
                BasicBlockNode bn = (BasicBlockNode) node;
                for(int i = 0;i < bn.statementsSize();i++) {
                    AST r = bn.getStatement(i);
                    if(r.getType() == JavaTokenTypes.VARIABLE_DEF) {
                        _processLocalVarDef(st, r, i);
                    }
                }
            } break;

            case FlowGraphNode.FOR_BEGIN: {
                AST v = node.getAbsTreeNode();
                v = v.getFirstChild();
                if(v.getType() != JavaTokenTypes.FOR_INIT) break;
                v = v.getFirstChild();
                while(v != null) {
                    if(v.getType() == JavaTokenTypes.VARIABLE_DEF) {
                        _processLocalVarDef(st, v, 0);
                    }
                    v = v.getNextSibling();
                }
            } break;
            
            case FlowGraphNode.CATCH_BEGIN: {
                AST v = node.getAbsTreeNode();
                v = v.getFirstChild();
                if(v.getType() == JavaTokenTypes.PARAMETER_DEF) {
                    _processLocalParDef(st, v);
                }
            } break;
            
            case FlowGraphNode.METHOD_CALL: {
                AST v = node.getAbsTreeNode();
                if(v != null && v.getType() == JavaTokenTypes.VARIABLE_DEF) {
                    _processLocalVarDef(st, v, 0);
                }
            } break;

            default: break;
        }
    }

    private void _processLocalVarDef(SymbolTable st, AST r, int statIndex) {
        AST v = r.getFirstChild();
        v = v.getNextSibling();

        AST ch = v.getFirstChild();
        if(ch.getType() == JavaTokenTypes.ARRAY_DECLARATOR) {
            int narray = 1;
            ch = ch.getFirstChild();
            while(ch.getType() == JavaTokenTypes.ARRAY_DECLARATOR) {
                narray++;
                ch = ch.getFirstChild();
            }
            _processIdent(v, r, ch, narray, statIndex, st);
        }
        else
        if(ch.getType() == JavaTokenTypes.IDENT || 
           ch.getType() == JavaTokenTypes.DOT) {

            _processIdent(v, r, ch, 0, statIndex, st);
        }
        else
        if(ch.getType() >= JavaTokenTypes.LITERAL_void && 
           ch.getType() <= JavaTokenTypes.LITERAL_double) {

            _processIdent(v, r, ch, 0, statIndex, st);
        }
    }

    private void _processLocalParDef(SymbolTable st, AST r) {
        AST v = r.getFirstChild();
        v = v.getNextSibling();
        AST ch = v.getFirstChild();
        if(ch.getType() == JavaTokenTypes.ARRAY_DECLARATOR) {
            int narray = 1;   
            ch = ch.getFirstChild();
            while(ch.getType() == JavaTokenTypes.ARRAY_DECLARATOR) {
                narray++;
                ch = ch.getFirstChild();   
            }

            _processIdent(v, r, ch, narray, 0, st);            
        }                        
        else
        if(ch.getType() == JavaTokenTypes.IDENT || 
           ch.getType() == JavaTokenTypes.DOT) {

            _processIdent(v, r, ch, 0, 0, st);
        }
        else
        if(ch.getType() >= JavaTokenTypes.LITERAL_void && 
           ch.getType() <= JavaTokenTypes.LITERAL_double) {

            _processIdent(v, r, ch, 0, 0, st);
        }    
    }

    private void _processIdent(AST vd, AST v, AST mo, int narray,
            int statIndex, SymbolTable st) {
        NameCollector text = new NameCollector();
        Util.inorderTraverse(mo, text);

        Class type = Util.getType(text.getName(), _currentInfo.getName(),
            _currentInfo.getPackage(), _currentInfo.getImports());

        mo = vd.getNextSibling();

        text = new NameCollector();
        Util.inorderTraverse(mo, text);
        
        STLocalVariableEntry entry = new STLocalVariableEntry(
            text.getName(), STEntry.LOCAL, v, narray);
        entry.setStatementIndex(statIndex);
        entry.setType(type);

        entry.setPrimitive(Util.isPrimitive(type));
        
        if(st.hasElement(entry.getName())) {
            System.out.println("Error : multiple definition " +
                " of local variable " + text.getName());
        }
        st.putElement(entry.getName(), entry);
    }

    private void _check() {
        Iterator ite = _currentFG.nodesIterator();
        while(ite.hasNext()) {
            FlowGraphNode n = (FlowGraphNode) ite.next();
            if(n.getType() == FlowGraphNode.SOURCE ||
               n.getType() == FlowGraphNode.SINK) continue;

            if(n.inputSize() == 0 && n.getType() != FlowGraphNode.CATCH_BEGIN &&
               n.getType() != FlowGraphNode.IF_END &&
               n.getType() != FlowGraphNode.TRY_END && 
               n.getType() != FlowGraphNode.FINALLY_BEGIN) {
                System.out.println("method " + _currentFG.toString() +  
                    " FG Check: node " + n.toString() + " has zero inputs");
            }

            if(n.outputSize() == 0) {
                System.out.println("method " + _currentFG.toString() + 
                    " FG Check: node " + n.toString() + " has zero outputs");
            }
        }
    }

    private void _build(AST stats) {
        _uniqueCount = 0;
        _uniqueSCCount = 0;
        _labels = new HashMap();
        _currentLabel = null;

        _skipMCTable = new HashMap();

        FlowGraphNode source = new FlowGraphNode(null, FlowGraphNode.SOURCE);
        source.setScope(1);
        _scopeNumber = 1;
        source.setUniqueNum(_uniqueCount++);
        _currentFG.addNode(source);
        _currentFG.setSource(source);
        _currentSource = source;

        FlowGraphNode sink = new FlowGraphNode(null, FlowGraphNode.SINK);
        sink.setUniqueNum(_uniqueCount++);
        _currentFG.setSink(sink);

        Stack entryScope = new Stack();
        Stack exitScope = new Stack();

        Stack SCStack = new Stack();

        AST v = stats;
        _processAStatement(v, entryScope, exitScope, SCStack);

        if(_currentSource != null && 
           _currentSource.getType() != FlowGraphNode.RETURN &&
           _currentSource.getType() != FlowGraphNode.THROW) {
            _currentFG.connect(_currentSource, sink);
        } 
        _currentFG.addNode(sink);
        sink.setScope(1);
    }

    /* Utilizado pelo processAStatement para quando for um VarDEf e o no
    criado um BB, criar o respectivo info */
    private void _addVarDefInfo(AST stat) {
        BasicBlockNode bb = (BasicBlockNode) _currentSource;
        StatementInfo info = bb.getStatementInfo(bb.statementsSize() - 1);

        VarDefStatementInfo vardef = new VarDefStatementInfo(bb, stat);
        vardef.setLabel(info.getLabel());
        bb.setStatementInfo(vardef, bb.statementsSize() - 1);
    }
    
    // Indica quando um novo escopo esta sendo aberto. Ai, o proximo no
    // a ser construido deve aumentar seu openSlist e setar essa variavel
    // para 0
    private int _openscope = 0;

    private void _openScope(FlowGraphNode node) {
        _scopeNumber++;
        //node.openScope();
    }

    private void _closeScope(FlowGraphNode node) {
        _scopeNumber--;
        //node.closeScope();
    }

    // indica que o proximo no criado deve chamar o openScope()
    private int _oscope = 0;

    private void _setScope(FlowGraphNode node) {
        if(_oscope > 0) {
            for(int i = 0;i < _oscope;i++) {
                node.openScope();
            }
            _oscope = 0;
        }
    }

    private void _processAStatement(AST stat, Stack entryScope,
            Stack exitScope, Stack SCStack) {
        if(stat == null) return;
        switch(stat.getType()) {

            case JavaTokenTypes.CLASS_DEF: {

                InnerClassNode inner = new InnerClassNode(stat, _currentInfo,
                    InnerClassNode.LOCAL);
                inner.setUniqueNum(_uniqueCount++);
                inner.setScope(_scopeNumber);
                if(_currentLabel != null) {
                    inner.setLabel(new String(_currentLabel));
                    _labels.put(inner.getLabel(), inner);
                    _currentLabel = null;
                }
                _setScope(inner);

                _currentFG.addNode(inner);
                _currentFG.connect(_currentSource, inner);
                _currentSource = inner;

            } break;

            case JavaTokenTypes.VARIABLE_DEF: {
                AST v = stat.getFirstChild();
                while(v.getNextSibling() != null) {
                    v = v.getNextSibling();
                }
                if(v.getType() == JavaTokenTypes.ASSIGN) {
                    v = v.getFirstChild();
                    while(v.getType() != JavaTokenTypes.EXPR) {
                        v = v.getFirstChild();
                    }
                    AST mc = v.getFirstChild();
                    if(mc.getType() == JavaTokenTypes.METHOD_CALL) {
                        _buildMethodCallNode(stat);
                        _skipMCTable.put(_currentSource, mc);
                    }
                    else
                    if(mc.getType() == JavaTokenTypes.TYPECAST) {
                        AST mc2 = mc.getFirstChild();
                        mc2 = mc2.getNextSibling();
                        if(mc2.getType() == JavaTokenTypes.METHOD_CALL) {
                            _buildMethodCallNode(stat);
                            _skipMCTable.put(_currentSource, mc2);                                                                                    
                        }
                        else {
                            _buildBasicBlockNode(stat);
                            _addVarDefInfo(stat);
                        }
                    }
                    else {
                        _buildBasicBlockNode(stat);
                        _addVarDefInfo(stat);
                    }
                }
                else {
                    _buildBasicBlockNode(stat);
                    _addVarDefInfo(stat);
                }
            } break;

            case JavaTokenTypes.EXPR: {
                AST n = stat.getFirstChild();
                if(n.getType() == JavaTokenTypes.METHOD_CALL) {
                    _buildMethodCallNode(n);
                }
                else
                if(n.getType() == JavaTokenTypes.ASSIGN) {
                    AST v = n.getFirstChild();
                    AST v2 = v.getNextSibling();
                    if(v2.getType() ==  JavaTokenTypes.METHOD_CALL) {
                        _buildMethodCallNode(stat);
                        _skipMCTable.put(_currentSource, v2);
                    }
                    else
                    if(v2.getType() == JavaTokenTypes.TYPECAST) {
                        AST v3 = v2.getFirstChild();
                        v3 = v3.getNextSibling();
                        if(v3.getType() == JavaTokenTypes.METHOD_CALL) {
                            _buildMethodCallNode(stat);
                            _skipMCTable.put(_currentSource, v3);
                        }
                        else {
                            _buildBasicBlockNode(stat);
                        }
                    }
                    else {
                        _buildBasicBlockNode(stat);
                    }
                }
                else {
                    _buildBasicBlockNode(stat);
                }
            } break;

            case JavaTokenTypes.LABELED_STAT: {
                AST v = stat.getFirstChild();
                _currentLabel = v.getText();

                v = v.getNextSibling();
                _processAStatement(v, entryScope, exitScope, SCStack);
            } break;

            case JavaTokenTypes.LITERAL_if: {
                IfBeginNode ifbeg = new IfBeginNode(stat);
                ifbeg.setUniqueNum(_uniqueCount++);
                ifbeg.setScope(_scopeNumber);
                if(_currentLabel != null) {
                    ifbeg.setLabel(new String(_currentLabel));
                    _labels.put(ifbeg.getLabel(), ifbeg);
                    _currentLabel = null;
                }
                _setScope(ifbeg);
                _currentFG.addNode(ifbeg);
                _currentFG.connect(_currentSource, ifbeg);

                IfEndNode ifend = new IfEndNode(stat);
                ifend.setUniqueNum(_uniqueCount++);
                ifend.setScope(_scopeNumber);

                _openScope(ifbeg);
                _oscope++;

                _currentSource = ifbeg;
                AST n = stat.getFirstChild();
                n = n.getNextSibling();
                _processAStatement(n, entryScope, exitScope, SCStack);

                // para detectar se o if tem continuacao
                boolean hascont = false;

                if(_currentSource == null) {
                    
                }
                else
                if(_currentSource.getType() == FlowGraphNode.RETURN ||
                   _currentSource.getType() == FlowGraphNode.BREAK ||
                   _currentSource.getType() == FlowGraphNode.CONTINUE ||
                   _currentSource.getType() == FlowGraphNode.THROW) {
                //if(_currentSource == null) {
                    _currentSource.closeScope();
                    ifbeg.setBreakScopeThen(_currentSource);
                }
                else {
                    FlowGraphEdge edge = _currentFG.connect(_currentSource, 
                        ifend);
                    IfBranchEdge ibinfo = new IfBranchEdge(true);
                    edge.setEdgeInfo(ibinfo);

                    hascont = true;
                    if(_currentSource != ifbeg) {
                        _currentSource.closeScope();
                    }
                }
                FlowGraphEdge edge = ifbeg.getOutputEdge(0);
                IfBranchEdge ibinfo = new IfBranchEdge(true);
                edge.setEdgeInfo(ibinfo);

                _oscope = 0;

                // Aqui nao pode ter o _openScope()
                _currentSource = ifbeg;
                n = n.getNextSibling();
                if(n != null) {
                    _oscope++;
                    _processAStatement(n, entryScope, exitScope, SCStack);

                    if(_currentSource == null) {

                    }
                    else
                    if(_currentSource.getType() == FlowGraphNode.RETURN ||
                       _currentSource.getType() == FlowGraphNode.BREAK ||
                       _currentSource.getType() == FlowGraphNode.CONTINUE ||
                       _currentSource.getType() == FlowGraphNode.THROW) {
                    //if(_currentSource == null) {
                        _currentSource.closeScope();
                        ifbeg.setBreakScopeElse(_currentSource);
                    }
                    else {
                        edge = _currentFG.connect(_currentSource, ifend);
                        ibinfo = new IfBranchEdge(false);
                        edge.setEdgeInfo(ibinfo);

                        hascont = true;
                        if(_currentSource != ifbeg) {
                            _currentSource.closeScope();
                        }
                    }
                }
                // nao tem o segundo caminho do if
                else {
                    edge = _currentFG.connect(ifbeg, ifend);
                    ibinfo = new IfBranchEdge(false);
                    edge.setEdgeInfo(ibinfo);

                    hascont = true;
                }
                _oscope = 0;

                edge = ifbeg.getOutputEdge(1);
                ibinfo = new IfBranchEdge(false);
                edge.setEdgeInfo(ibinfo);

                //if(hascont) {
                    ifbeg.setIfEnd(ifend);
                    _currentFG.addNode(ifend);
                    _currentSource = ifend;
                //}
                //else {
                //    _currentSource = null;
                // }

                // Desfaz o _openScope()
                _scopeNumber--;
            } break;

            case JavaTokenTypes.LITERAL_for: {
                ForBeginNode beg = new ForBeginNode(stat);
                beg.setUniqueNum(_uniqueCount++);
                beg.setScope(_scopeNumber);
                if(_currentLabel != null) {
                    beg.setLabel(new String(_currentLabel));
                    _labels.put(beg.getLabel(), beg);
                    _currentLabel = null;
                }
                _setScope(beg);
                _currentFG.addNode(beg);
                _currentFG.connect(_currentSource, beg);

                ForEndNode end = new ForEndNode(stat);
                end.setUniqueNum(_uniqueCount++);
                end.setScope(_scopeNumber);
                beg.setForEnd(end);

                _openScope(beg);
                _oscope++;

                entryScope.push(beg);
                exitScope.push(end);

                _currentSource = beg;

                AST fs = stat.getFirstChild();
                fs = fs.getNextSibling();
                fs = fs.getNextSibling();
                fs = fs.getNextSibling();
                _processAStatement(fs, entryScope, exitScope, SCStack);

                exitScope.pop();
                entryScope.pop();

                _closeScope(_currentSource);
                if(_currentSource != beg) {
                    _currentSource.closeScope();
                }
                else {
                    _oscope = 0;
                }
                if(_currentSource.getType() != FlowGraphNode.RETURN &&
                    _currentSource.getType() != FlowGraphNode.BREAK &&
                    _currentSource.getType() != FlowGraphNode.CONTINUE &&
                    _currentSource.getType() != FlowGraphNode.THROW) {
                //if(_currentSource != null) {
                    FlowGraphEdge edge = _currentFG.connect(_currentSource, 
                        beg);
                    BackEdge be = new BackEdge();
                    edge.setEdgeInfo(be);
                }
                _currentFG.connect(beg, end);
                _currentFG.addNode(end);
                _currentSource = end;
            } break;

            case JavaTokenTypes.LITERAL_while: {
                WhileBeginNode beg = new WhileBeginNode(stat);
                beg.setUniqueNum(_uniqueCount++);
                beg.setScope(_scopeNumber);
                if(_currentLabel != null) {
                    beg.setLabel(new String(_currentLabel));
                    _labels.put(beg.getLabel(), beg);
                    _currentLabel = null;
                }
                _setScope(beg);
                _currentFG.addNode(beg);
                _currentFG.connect(_currentSource, beg);

                WhileEndNode end = new WhileEndNode(stat);
                end.setUniqueNum(_uniqueCount++);
                end.setScope(_scopeNumber);
                beg.setWhileEnd(end);

                _openScope(beg);
                _oscope++;

                entryScope.push(beg);
                exitScope.push(end);

                _currentSource = beg;

                AST ws = stat.getFirstChild();
                ws = ws.getNextSibling();
                _processAStatement(ws, entryScope, exitScope, SCStack);

                // So pode ter um outputedge!
                FlowGraphEdge outedge = beg.getOutputEdge(0);
                if(outedge != null) {
                    LoopEdge le = new LoopEdge(EdgeInfo.LOOP_BODY);
                    outedge.setEdgeInfo(le);
                }

                exitScope.pop();
                entryScope.pop();

                _closeScope(_currentSource);
                if(_currentSource != beg) {
                    _currentSource.closeScope();
                }
                else {
                    _oscope = 0;
                }
                if(_currentSource.getType() != FlowGraphNode.RETURN &&
                    _currentSource.getType() != FlowGraphNode.BREAK &&
                    _currentSource.getType() != FlowGraphNode.CONTINUE &&
                    _currentSource.getType() != FlowGraphNode.THROW) {
                //if(_currentSource != null) {
                    FlowGraphEdge edge = _currentFG.connect(_currentSource,
                        beg);
                    BackEdge be = new BackEdge();
                    edge.setEdgeInfo(be);
                }
                FlowGraphEdge fe = _currentFG.connect(beg, end);
                fe.setEdgeInfo(new LoopEdge(EdgeInfo.LOOP_EXIT));

                _currentFG.addNode(end);
                _currentSource = end;
            } break;

            case JavaTokenTypes.LITERAL_do: {
                // Criacao do no de inicio do laco
                DoBeginNode beg = new DoBeginNode(stat);
                beg.setUniqueNum(_uniqueCount++);
                beg.setScope(_scopeNumber);
                if(_currentLabel != null) {
                    beg.setLabel(new String(_currentLabel));
                    _labels.put(beg.getLabel(), beg);
                    _currentLabel = null;
                }
                _setScope(beg);
                _currentFG.addNode(beg);
                _currentFG.connect(_currentSource, beg);

                // Criacao do no de teste da condicao. O scopre tree node
                // e o mesmo do beg, pois as declaracao internas do laco
                // nao sao visiveis
                DoConditionNode cond = new DoConditionNode(stat);
                cond.setUniqueNum(_uniqueCount++);
                cond.setScope(_scopeNumber);
                _currentFG.addNode(cond);

                beg.setConditionNode(cond);

                // Criacao do no de termino do laco
                DoEndNode end = new DoEndNode(stat);
                end.setUniqueNum(_uniqueCount++);
                end.setScope(_scopeNumber);
                beg.setDoEnd(end);

                _openScope(beg);
                _oscope++;

                entryScope.push(cond);
                exitScope.push(end);

                _currentSource = beg;
                AST ds = stat.getFirstChild();
                _processAStatement(ds, entryScope, exitScope, SCStack);

                exitScope.pop();
                entryScope.pop();

                _closeScope(_currentSource);
                if(_currentSource != beg) {
                    _currentSource.closeScope();
                }
                else {
                    _oscope = 0;
                }
                if(_currentSource.getType() != FlowGraphNode.RETURN &&
                    _currentSource.getType() != FlowGraphNode.BREAK &&
                    _currentSource.getType() != FlowGraphNode.CONTINUE &&
                    _currentSource.getType() != FlowGraphNode.THROW) {
                //if(_currentSource != null) {
                    _currentFG.connect(_currentSource, cond);
                }
                FlowGraphEdge edge = _currentFG.connect(cond, beg);
                BackEdge be = new BackEdge();
                edge.setEdgeInfo(be);
                _currentFG.connect(cond, end);

                _currentFG.addNode(end);
                _currentSource = end;
            } break;

            case JavaTokenTypes.LITERAL_break: {
                BreakNode bn = new BreakNode(stat);
                bn.setUniqueNum(_uniqueCount++);
                bn.setScope(_scopeNumber);
                if(_currentLabel != null) {
                    bn.setLabel(new String(_currentLabel));
                    _labels.put(bn.getLabel(), bn);
                    _currentLabel = null;
                }
                _setScope(bn);
                _currentFG.addNode(bn);
                _currentFG.connect(_currentSource, bn);

                if(exitScope.size() == 0) {
                    System.out.println("Error: invalid placed break statement");
                    return;
                }

                FlowGraphNode fn;
                AST v = stat.getFirstChild();
                if(v != null) {
                    String label = v.getText();
                    fn = (FlowGraphNode) _labels.get(label);
                }
                else {
                    fn = (FlowGraphNode) exitScope.peek();
                }
                _currentFG.connect(bn, fn);
                _currentSource = bn;
                //_currentSource = null;
            } break;

            case JavaTokenTypes.LITERAL_continue: {
                ContinueNode cn = new ContinueNode(stat);
                cn.setUniqueNum(_uniqueCount++);
                cn.setScope(_scopeNumber);
                if(_currentLabel != null) {
                    cn.setLabel(new String(_currentLabel));
                    _labels.put(cn.getLabel(), cn);
                    _currentLabel = null;
                }
                _setScope(cn);
                _currentFG.addNode(cn);
                _currentFG.connect(_currentSource, cn);

                if(entryScope.size() == 0) {
                    System.out.println("Error: invalid placed continue statement");
                    return;
                }

                FlowGraphNode fn;
                AST v = stat.getFirstChild();
                if(v != null) {
                    String label = v.getText();
                    fn = (FlowGraphNode) _labels.get(label);
                }
                else {
                    // ATENCAO... a linha era fn = (FlowGraphNode) exitScope.peek();
                    // mais eu mudei, pois isso ta errado... tenho que ver
                    // se nao tem mais consequencias
                    fn = (FlowGraphNode) entryScope.peek();
                }
                
                // tem que ver se e back mesmo ...
                FlowGraphEdge edge = _currentFG.connect(cn, fn);
                BackEdge be = new BackEdge();
                edge.setEdgeInfo(be);
                _currentSource = cn;
                //_currentSource = null;
            } break;

            case JavaTokenTypes.LITERAL_return: {
                ReturnNode retn = new ReturnNode(stat);
                retn.setUniqueNum(_uniqueCount++);
                retn.setScope(_scopeNumber);

                if(_currentLabel != null) {
                    retn.setLabel(new String(_currentLabel));
                    _labels.put(retn.getLabel(), retn);
                    _currentLabel = null;
                }
                _setScope(retn);
                _currentFG.connect(_currentSource, retn);
                _currentFG.connect(retn, _currentFG.getSink());

                _currentFG.addNode(retn);
                _currentSource = retn;
                //_currentSource = null;
            } break;

            case JavaTokenTypes.LITERAL_switch: {
                SwitchBeginNode beg = new SwitchBeginNode(stat);
                beg.setUniqueNum(_uniqueCount++);
                beg.setScope(_scopeNumber);
                if(_currentLabel != null) {
                    beg.setLabel(new String(_currentLabel));
                    _labels.put(beg.getLabel(), beg);
                    _currentLabel = null;
                }
                _setScope(beg);
                _currentFG.addNode(beg);
                _currentFG.connect(_currentSource, beg);

                SwitchEndNode end = new SwitchEndNode(stat);
                end.setUniqueNum(_uniqueCount++);
                end.setScope(_scopeNumber);
                end.setSwitchBegin(beg);

                beg.setSwitchEnd(end);
                _currentFG.addNode(end);

                _currentSource = beg;

                //O switch nao abre escopo ... so cada case ...
                //_openScope(beg);

                /*
                _scopeNumber++; */

                // nao tem entryscope, porque se tiver continue, e para um loop externo                
                //entryScope.push(end);
                // break e para o fim do switch...
                exitScope.push(end);

                FlowGraphNode nobreak = null;

                // Processa cada case
                AST n = stat.getFirstChild();
                n = n.getNextSibling();
                while(n != null) {
                    // processa a condicao do case
                    AST cg = n.getFirstChild();
                    // No caso que for condicao: e vazio, entao tem que
                    // armazenar todos os casos em uma lista.
                    LinkedList conditions = new LinkedList();
                    if(cg.getType() == JavaTokenTypes.LITERAL_case) {
                        while(cg.getType() != JavaTokenTypes.SLIST) {
                            conditions.add(cg.getFirstChild());
                            cg = cg.getNextSibling();
                        }
                        //beg.addCaseCondition(conditions);
                    }
                    else
                    if(cg.getType() == JavaTokenTypes.LITERAL_default) {
                        //beg.addCaseCondition(null);
                        cg = cg.getNextSibling();
                    }

                    // O Scopo e aberto porque cg e uma SLIST....
                    _processAStatement(cg, entryScope, exitScope, SCStack);
                    
                    // A Conexcao com o end do switch vem dos breaks...
                    
                    // O case anterior nao teve break, entao tem que 
                    // conectar em sequencia
                    if(nobreak != null && _currentSource != beg && 
                       nobreak.getType() != FlowGraphNode.RETURN &&
                       nobreak.getType() != FlowGraphNode.CONTINUE &&
                       nobreak.getType() != FlowGraphNode.THROW) {
                        FlowGraphNode t = 
                            beg.getOutputNode(beg.outputSize() - 1);
                        _currentFG.connect(nobreak, t);
                        nobreak = null;

                        beg.setCaseEndNode(beg.outputSize() - 2, t);
                    }

                    if(_currentSource != beg) {
                        FlowGraphEdge edge = beg.getOutputEdge(beg.outputSize() - 1);
                        SwitchEdge einfo = new SwitchEdge(conditions);
                        edge.setEdgeInfo(einfo);
                        if(_currentSource.getType() != FlowGraphNode.BREAK &&
                           _currentSource.getType() != FlowGraphNode.RETURN &&
                           _currentSource.getType() != FlowGraphNode.THROW) {
                            nobreak = _currentSource;
                        }
                        beg.addCaseEndNode(end);
                    }
                    else {
                       // Acho que isso nunca ocorre
                    }
                    _currentSource = beg;
                    //_currentSCNode = scnode;
                    n = n.getNextSibling();
                }

                if(nobreak != null &&
                   nobreak.getType() != FlowGraphNode.RETURN &&
                   nobreak.getType() != FlowGraphNode.CONTINUE &&
                   nobreak.getType() != FlowGraphNode.THROW) {
                    _currentFG.connect(nobreak, end);
                }
                _currentSource = end;
            } break;

            case JavaTokenTypes.LITERAL_throw: {
                ThrowNode th = new ThrowNode(stat);
                th.setUniqueNum(_uniqueCount++);
                th.setScope(_scopeNumber);

                if(_currentLabel != null) {
                    th.setLabel(new String(_currentLabel));
                    _labels.put(th.getLabel(), th);
                    _currentLabel = null;
                }
                _setScope(th);
                _currentFG.connect(_currentSource, th);
                _currentFG.connect(th, _currentFG.getSink());

                _currentFG.addNode(th);
                _currentSource = th;
                //_currentSource = null;
            } break;
            
            case JavaTokenTypes.LITERAL_synchronized: {
                SyncBeginNode beg = new SyncBeginNode(stat);
                beg.setUniqueNum(_uniqueCount++);
                beg.setScope(_scopeNumber);
                if(_currentLabel != null) {
                    beg.setLabel(new String(_currentLabel));
                    _labels.put(beg.getLabel(), beg);
                    _currentLabel = null;
                }
                _setScope(beg);
                _currentFG.addNode(beg);
                _currentFG.connect(_currentSource, beg);

                SyncEndNode end = new SyncEndNode(stat);
                end.setUniqueNum(_uniqueCount++);
                end.setScope(_scopeNumber);
                beg.setSyncEnd(end);
                _currentFG.addNode(end);

                _currentSource = beg;
                
                /*_scopeNumber++;
                 */
                _openScope(beg);
                _oscope++;
                
                AST ast = stat.getFirstChild();
                ast = ast.getNextSibling();
                _processAStatement(ast, entryScope, exitScope, SCStack);
                
                _closeScope(_currentSource);
                if(_currentSource != beg) {
                    _currentSource.closeScope();
                }
                else {
                    _oscope = 0;
                }

                if(_currentSource.getType() != FlowGraphNode.RETURN &&
                   _currentSource.getType() != FlowGraphNode.THROW) {
                //if(_currentSource != null) {
                    _currentFG.connect(_currentSource, end);
                }
                _currentSource = end;
            } break;
            
            case JavaTokenTypes.LITERAL_try: {
                _buildTryNode(stat, SCStack, entryScope, exitScope);
            } break;
            
            case JavaTokenTypes.LITERAL_finally: {
                // Constroi o Try
                AST v = stat.getFirstChild();
                // ok, uma improvisacao. Preciso determinar o no do try,
                // para associar o finally. Faco isso a partir do 
                // currentsource atual
                FlowGraphNode savecsource = _currentSource;
                _buildTryNode(v, SCStack, entryScope, exitScope);

                savecsource = savecsource.getOutputNode(0);
                TryBeginNode tbeg = null;
                if(savecsource.getType() == FlowGraphNode.TRY_BEGIN) {
                     tbeg = (TryBeginNode) savecsource;
                }
                else {
                    System.out.println("Flow Graph build: savecsource nao e " +
                        " um try begin!!!");
                    return;
                }

                // tbeg.setTryEnd(tryend);
                // _currentSource = tryend;   // O Currentsource pode ser null!

                // Constroi o bloco do finally
                v = v.getNextSibling();
                FinallyBeginNode fbeg = new FinallyBeginNode(stat);
                fbeg.setUniqueNum(_uniqueCount++);
                fbeg.setScope(_scopeNumber);
                if(_currentLabel != null) {
                    fbeg.setLabel(new String(_currentLabel));
                    _labels.put(fbeg.getLabel(), fbeg);
                    _currentLabel = null;
                }
                _setScope(fbeg);
                _currentFG.addNode(fbeg);
                if(_currentSource != null) {
                    _currentFG.connect(_currentSource, fbeg);
                }

                FinallyEndNode fend = new FinallyEndNode(stat);
                fend.setUniqueNum(_uniqueCount++);
                fend.setScope(_scopeNumber);
                fbeg.setFinallyEnd(fend);

                _currentSource = fbeg;

                _openScope(fbeg);
                _oscope++;

                _processAStatement(v, entryScope, exitScope, SCStack);

                _closeScope(_currentSource);
                if(_currentSource != fbeg) {
                    _currentSource.closeScope();
                }
                else {
                    _oscope = 0;
                }

                //if(_currentSource != null) {
                if(_currentSource.getType() != FlowGraphNode.RETURN &&
                   _currentSource.getType() != FlowGraphNode.THROW) {
                    _currentFG.connect(_currentSource, fend);
                    _currentFG.addNode(fend);
                    _currentSource = fend;
                }
                else {
                    _currentSource = null;
                }
                tbeg.setFinally(fbeg);
            } break;

            case JavaTokenTypes.SLIST: {
                AST v = stat.getFirstChild();
                while(v != null) {
                    if(v.getType() == JavaTokenTypes.SLIST) {
                        //_scopeNumber++;
                        
                        //_openscope++;

                        _openScope(_currentSource);
                        _oscope++;
                        _processAStatement(v, entryScope, exitScope, SCStack);

                        _closeScope(_currentSource);
                        _currentSource.closeScope();

                        // Se tiver uma lista vazia, isto anula o valor do oscope
                        _oscope = 0;
                    }
                    else {
                        _processAStatement(v, entryScope, exitScope, SCStack);
                    }

                    if(_currentSource == null ||
                       _currentSource.getType() == FlowGraphNode.RETURN ||
                       _currentSource.getType() == FlowGraphNode.THROW) return;
                    //if(_currentSource == null) return;
                    v = v.getNextSibling();
                }
            } break;

            case JavaTokenTypes.CTOR_CALL:

            case JavaTokenTypes.SUPER_CTOR_CALL: {
                MethodCallNode mc = new MethodCallNode(stat);
                mc.setUniqueNum(_uniqueCount++);
                mc.setScope(_scopeNumber);
                if(_currentLabel != null) {
                    mc.setLabel(_currentLabel);
                    _labels.put(mc.getLabel(), mc);
                    _currentLabel = null;
                }
                _setScope(mc);
                _currentFG.addNode(mc);

                _currentFG.connect(_currentSource, mc);
                _currentSource = mc;
            } break;

            case JavaTokenTypes.EMPTY_STAT: {
                return;
            }

            default: {
                System.out.println("Build flow graph -> Statement " +
                    " not treated .. " + stat.toStringTree());
            }
        }
    }

    private void  _buildTryNode(AST stat, Stack SCStack, Stack entryScope,
            Stack exitScope) {
        TryBeginNode beg = new TryBeginNode(stat);
        beg.setUniqueNum(_uniqueCount++);
        beg.setScope(_scopeNumber);
        if(_currentLabel != null) {
            beg.setLabel(new String(_currentLabel));
            _labels.put(beg.getLabel(), beg);
            _currentLabel = null;
        }
        _setScope(beg);
        _currentFG.addNode(beg);
        _currentFG.connect(_currentSource, beg);
        _currentSource = beg;

        TryEndNode end = new TryEndNode(stat);
        end.setUniqueNum(_uniqueCount++);
        end.setScope(_scopeNumber);

        _openScope(beg);
        _oscope++;

        AST ast = stat.getFirstChild();
        _processAStatement(ast, entryScope, exitScope, SCStack);

        _closeScope(_currentSource);
        if(_currentSource != beg) {
            _currentSource.closeScope();
        }
        else {
            _oscope = 0;
        }
        if(_currentSource.getType() != FlowGraphNode.RETURN &&
           _currentSource.getType() != FlowGraphNode.THROW) {
        //if(_currentSource != null) {
            _currentFG.connect(_currentSource, end);
        }

        // Cria os nos de catch
        ast = ast.getNextSibling();
        while(ast != null) {
            if(ast.getType() == JavaTokenTypes.LITERAL_catch) {
                CatchBeginNode cbeg = new CatchBeginNode(ast);
                String typen = cbeg.getTypeName();
                Class t = Util.getType(typen, null, _currentInfo.getPackage(),
                    _currentInfo.getImports());
                /*
                if(Util.isSubClass(t, java.lang.RuntimeException.class)) {
                    System.out.println("WARNING ... Contains a Runtime Exception " +
                        " catch ....");
                    //ast = ast.getNextSibling();
                    //continue;
                } */

                cbeg.setUniqueNum(_uniqueCount++);
                cbeg.setScope(_scopeNumber);
                if(_currentLabel != null) {
                    beg.setLabel(new String(_currentLabel));
                    _labels.put(beg.getLabel(), beg);
                    _currentLabel = null;
                }
                _setScope(cbeg);
                _currentFG.addNode(cbeg);
                beg.addCatch(cbeg);

                _currentSource = cbeg;
                //_scopeNumber++;

                _openScope(cbeg);
                _oscope++;

                AST v = ast.getFirstChild();
                v = v.getNextSibling();
                _processAStatement(v, entryScope, exitScope, SCStack);

                _closeScope(_currentSource);
                if(_currentSource != cbeg) {
                    _currentSource.closeScope();
                }
                else {
                    _oscope = 0;
                }

                if(_currentSource.getType() != FlowGraphNode.RETURN &&
                     _currentSource.getType() != FlowGraphNode.THROW &&
                     _currentSource.getType() != FlowGraphNode.BREAK) {
                //if(_currentSource != null) {
                    CatchEndNode cend = new CatchEndNode(stat);
                    cend.setUniqueNum(_uniqueCount++);
                    cend.setScope(_scopeNumber);
                    cbeg.setCatchEnd(cend);
                    _currentFG.addNode(cend);

                    _currentFG.connect(_currentSource, cend);
                    _currentFG.connect(cend, end);
                }
                else {
                    cbeg.setCatchEnd(null);
                }
            }
            else
            if(ast.getType() == JavaTokenTypes.LITERAL_finally) {
                System.out.println("FINALLY NOT SUPPORTED!!!");
            }
            ast = ast.getNextSibling();
        }

        // Se nesse ponto, o end nao tiver entradas, entao e porque
        // nao tem catch e o corpo do try ta com return.
        if(end.inputSize() != 0) {
            _currentFG.addNode(end);
            beg.setTryEnd(end);
            _currentSource = end;
        }
        else {
            beg.setTryEnd(null);
            _currentSource = null;
        }
    }

    private void _buildMethodCallNode(AST stat) {
        MethodCallNode mc = new MethodCallNode(stat);
        mc.setUniqueNum(_uniqueCount++);
        mc.setScope(_scopeNumber);
        if(_currentLabel != null) {
            mc.setLabel(_currentLabel);
            _labels.put(mc.getLabel(), mc);
            _currentLabel = null;
        }
        _setScope(mc);
        _currentFG.addNode(mc);

        _currentFG.connect(_currentSource, mc);
        _currentSource = mc;
    }

    private void _buildBasicBlockNode(AST stat) {
        // Tem que ser um bb que nao abra e nem feche um bloco
        if(_currentSource.getType() == FlowGraphNode.BASIC_BLOCK &&
           _currentSource.getOpenedScopes() == 0 &&
           _currentSource.getClosedScopes() == 0 &&
           _oscope == 0) {
            BasicBlockNode bb = (BasicBlockNode) _currentSource;
            bb.addStatement(stat);
            if(_currentLabel != null) {
                bb.addLabel(_currentLabel);
                _labels.put(bb.getLabel(bb.statementsSize() - 1), bb);
                _currentLabel = null;
            }
        }
        else {
            BasicBlockNode bb = new BasicBlockNode(stat);
            bb.setUniqueNum(_uniqueCount++);
            bb.setScope(_scopeNumber);
            if(_currentLabel != null) {
                bb.addLabel(_currentLabel);
                _labels.put(bb.getLabel(bb.statementsSize() - 1), bb);
                _currentLabel = null;
            }
            _setScope(bb);
            _currentFG.addNode(bb);

            _currentFG.connect(_currentSource, bb);
            _currentSource = bb;
        }
    }

    /* coleta os identificadores de variaveis e parametros. Utilizado para
    determinar um nome valido para uma nova variavel. */
    private void _collectIdentifiers() {
        _collector = new IdentCollector();
        /*
        try {
            _collector.slist(_stats);
        }
        catch(RecognitionException ex) {
            System.out.println(ex);
        } */
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private variables                  //// 

    private FlowGraph _currentFG;
    private FlowGraphNode _currentSource;
    private int _scopeNumber;
    private String _currentLabel;
    private HashMap _labels;

    private int _uniqueCount;
    private int _uniqueSCCount;
    private ClassData _currentInfo;
 
    private IdentCollector _collector;
   
    // Eu guardo nesta tabela o AST da chamada de um metodo. Isso e
    // utilizado pelo extractMethodcalls,para nao extrair uma chamada
    // de metodo com vardef e assign.
    private HashMap _skipMCTable;
}
